<script lang="ts">
    import { onMount } from "svelte";
    import UiSpace from "$lib/core/UiSpace.svelte";
    import DataTable from "$lib/core/space-table/data-table.svelte";
    import type { Space, SpaceState, SpaceUser, User } from "$lib/core/index";
    import {
        axiosIns,
        space_perms_key,
        space_pings_key,
        space_patch_key,
        toastErr,
        debugBy,
    } from "$lib/core/index";
    import mqtt, { type MqttClient } from "mqtt";
    import { toStore } from "svelte/store";
    import * as jsonpatch from "fast-json-patch";
    import type { Observer } from "fast-json-patch";

    let user = $state<User>();
    let space = $state<Space>();
    let spaceStore = toStore(
        () => space,
        (s) => (space = s),
    );
    let stateObserver: Observer<SpaceState> | undefined = undefined;
    let stateSnapshot: SpaceState | undefined = undefined;
    let patchState = (update: (s: Space) => void) => {
        if (space && stateObserver) {
            stateSnapshot = space.state;
            update(space);
            let patch = jsonpatch.generate(stateObserver);
            console.debug("state patch:", patch);
            axiosIns
                .post(`/spaces/state/patch/${space.id}`, { patch })
                .then(() => {
                    stateSnapshot = undefined;
                })
                .catch((err) => {
                    toastErr(err);
                    if (space && stateSnapshot) {
                        space.state = stateSnapshot;
                    }
                });
        }
    };
    let mqttCli = $state<MqttClient>();
    let enterSpace = async (s: Space) => {
        try {
            await axiosIns.post(`/spaces/enter/${s.id}`);
            let res = await axiosIns.get(`/spaces/state/${s.id}`);
            console.debug(res.data);
            space = { ...s, state: res.data };
            let client = mqtt
                .connect("ws://broker.emqx.io:8083/mqtt", {
                    protocolVersion: 5,
                })
                .subscribe([
                    space_patch_key(s.id),
                    space_perms_key(s.id),
                    space_pings_key(s.id),
                ]);
            client.handleMessage = (msg, cb) => {
                if (space) {
                    if (msg.topic === space_perms_key(space.id)) {
                        let users: SpaceUser[] = JSON.parse(
                            msg.payload.toString(),
                        );
                        space.users = users;
                    }
                    if (msg.topic === space_patch_key(space.id)) {
                    }
                    if (msg.topic === space_pings_key(space.id)) {
                        let userIds: number[] = JSON.parse(
                            msg.payload.toString(),
                        );
                        space.users.forEach(
                            (user) => (user.online = userIds.includes(user.id)),
                        );
                    }
                }
                cb();
            };
            mqttCli = client;
        } catch (err) {
            return toastErr(err);
        }
    };
    let leaveSpace = async (s: Space) => {
        try {
            if (mqttCli) {
                mqttCli.unsubscribe([
                    space_patch_key(s.id),
                    space_perms_key(s.id),
                    space_pings_key(s.id),
                ]);
                mqttCli.end();
            }
            await axiosIns.post(`/spaces/leave/${s.id}`);
            space = undefined;
        } catch (err) {
            return toastErr(err);
        }
    };

    onMount(() => {
        if (import.meta.env.DEV) {
            switch (import.meta.env.VITE_USER_ROLE) {
                case "teacher":
                    user = { id: 1, username: "teacher", avatar: "" };
                    break;
                case "student":
                    user = { id: 2, username: "student", avatar: "" };
                    break;
            }
        } else {
            // TODO fetch user info
        }
        let unsubSpaceStore = spaceStore.subscribe(
            (s) => {
                stateObserver?.unobserve();
                if (s) {
                    stateObserver = jsonpatch.observe<SpaceState>(s.state);
                }
            },
            () => {
                stateObserver?.unobserve();
            },
        );
        if (space) {
            unsubSpaceStore();
            leaveSpace(space);
        }
    });

    // $inspect(space).with(debugBy("page"));
</script>

<main class="w-full h-full relative">
    {#if user}
        {#if space}
            <UiSpace bind:space {user} {mqttCli} {leaveSpace} {patchState} />
        {:else}
            <div class="w-full h-full flex justify-center items-center">
                <div class="w-1/2 min-w-[800px] h-1/2 min-h-[600px]">
                    <DataTable userId={user.id} {enterSpace} />
                </div>
            </div>
        {/if}
    {/if}
</main>
